package ray.math;

/**********************************************************************************
 * GENERAL CONTRACT FOR ALL MATH CLASSES.
 * 
 * -- The destination of every method is assumed to be the object the method was
 * called on. For example:
 * 
 * c.add(a,b) means c = a + b.
 * 
 * -- Whenever one operand of a binary operand is missing, it is assumed to be
 * the object the method was called upon. For example:
 * 
 * c.add(a) means c = c + a.
 * 
 ********************************************************************************* 
 * 
 * The Vector3 class represents a 3 dimension vector of doubles.
 * 
 * @author ags
 */
public class Vector3 extends Tuple3 {

	/**
	 * Default constructor. Uses explicit constructor to create a zero vector.
	 */
	public Vector3() {

		super(0, 0, 0);
	}

	/**
	 * Copy constructor. This constructor simply calls the explicit constructor
	 * with the necessary fields from the input as parameters.
	 * 
	 * @param newTuple
	 *            The vector to copy.
	 */
	public Vector3(Tuple3 newTuple) {

		super(newTuple.x, newTuple.y, newTuple.z);
	}

	/**
	 * The explicit constructor. This is the only constructor with any real code
	 * in it. Values should be set here, and any variables that need to be
	 * calculated should be done here.
	 * 
	 * @param newX
	 *            The x value of the new vector.
	 * @param newY
	 *            The y value of the new vector.
	 * @param newZ
	 *            The z value of the new vector.
	 */
	public Vector3(double newX, double newY, double newZ) {

		super(newX, newY, newZ);

	}

	/**
	 * Sets this vector to the cross product of op1 and op2
	 * 
	 * @param op1
	 * @param op2
	 */
	public void cross(Vector3 op1, Vector3 op2) {

		this.x = op1.y * op2.z - op1.z * op2.y;
		this.y = op1.z * op2.x - op1.x * op2.z;
		this.z = op1.x * op2.y - op1.y * op2.x;

	}
	
	public Vector3 cross(Vector3 op1){
		Vector3 ans = new Vector3(this);
		ans.cross(op1);
		return ans;
	}

	/**
	 * Returns the dot product of this Vector3 object and the parameter Vector3.
	 * 
	 * @param rhs
	 *            The right hand operand.
	 * @return The dot product of this Vector3 object and the parameter Vector3.
	 */
	public double dot(Vector3 rhs) {

		return x * rhs.x + y * rhs.y + z * rhs.z;
	}

	/**
	 * Returns the length of this vector.
	 * 
	 * @return The length of this vector.
	 */
	public double length() {

		return Math.sqrt(x * x + y * y + z * z);

	}

	/**
	 * Returns the length squared of this vector. Very useful if only comparison
	 * of lengths is needed since it saves the square root.
	 * 
	 * @return the length squared of this vector3
	 */
	public double lengthsquared() {

		return x * x + y * y + z * z;

	}

	/**
	 * This method will normalize this Vector3 so that its length is 1.0. If the
	 * length of the Vector3 is 0, no action is taken.
	 */
	public void normalize() {

		double dist = Math.sqrt(x * x + y * y + z * z);
		if (dist != 0) {
			x /= dist;
			y /= dist;
			z /= dist;
		}
	}

	/**
	 * Add a Vector3 to this Vector3
	 * 
	 * @param vector
	 *            the Vector3 to add
	 */
	public void add(Vector3 vector) {

		this.x += vector.x;
		this.y += vector.y;
		this.z += vector.z;

	}

	/**
	 * Add the values of Vector3 v1 and Vector3 v2 and store the sum in this
	 * Vector3.
	 * 
	 * @param v1
	 *            the first operand
	 * @param v2
	 *            the second operand
	 */
	public void add(Vector3 v1, Vector3 v2) {

		this.x = v1.x + v2.x;
		this.y = v1.y + v2.y;
		this.z = v1.z + v2.z;

	}

	/**
	 * Substract a Vector3 from this Vector3
	 * 
	 * @param vector
	 *            the Tuple3 to subtract
	 */
	public void sub(Vector3 vector) {

		this.x -= vector.x;
		this.y -= vector.y;
		this.z -= vector.z;

	}

	/**
	 * Subtract one Point3 from another Point3 and set as this Vector
	 * 
	 * @param p1
	 *            the first operand
	 * @param p2
	 *            the second operand
	 */
	public void sub(Point3 p1, Point3 p2) {

		this.x = p1.x - p2.x;
		this.y = p1.y - p2.y;
		this.z = p1.z - p2.z;

	}

	/**
	 * Add a scalar multiple of a Vector3 to this Vector3
	 * 
	 * @param scale
	 *            the scale factor
	 * @param vector
	 *            the vector to scale add
	 */
	public void scaleAdd(double scale, Tuple3 vector) {

		this.x += scale * vector.x;
		this.y += scale * vector.y;
		this.z += scale * vector.z;

	}

	public Vector3 reverse() {
		Vector3 ans = new Vector3(this);
		ans.scale(-1);
		return ans;
	}

	public Vector3 multiply(double op1) {
		Vector3 ans = new Vector3(this);
		ans.scale(op1);
		return ans;
	}

	public Vector3(Point3 start, Point3 end) {
		super(end.x - start.x,end.y - start.y,end.z - start.z);
	}

}